'use client';
import { useState, useEffect } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import { Search, Mail, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
import {
getMPVerificationStatus,
requestMPVerification,
cancelMPVerificationRequest,
} from '@/actions/mp-verification';
import { MPVerifiedBadge } from '@/components/MPVerifiedBadge';
interface MP {
parl_mp_id: number;
name: string;
party: string;
riding: string;
photo_url?: string;
}
export default function VerifyMPPage() {
const { user } = useAuth();
const router = useRouter();
const searchParams = useSearchParams();
// URL params for success/error messages
const success = searchParams.get('success');
const error = searchParams.get('error');
const mpName = searchParams.get('mp');
// State
const [mpSearch, setMPSearch] = useState('');
const [searchResults, setSearchResults] = useState<MP[]>([]);
const [selectedMP, setSelectedMP] = useState<MP | null>(null);
const [isSearching, setIsSearching] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const [verificationStatus, setVerificationStatus] = useState<any>(null);
const [statusLoading, setStatusLoading] = useState(true);
// Fetch current verification status
useEffect(() => {
if (user) {
fetchVerificationStatus();
}
}, [user]);
const fetchVerificationStatus = async () => {
setStatusLoading(true);
const result = await getMPVerificationStatus();
if (result.success && result.data) {
setVerificationStatus(result.data);
}
setStatusLoading(false);
};
// Search for MPs
const handleSearch = async () => {
if (!mpSearch.trim()) return;
setIsSearching(true);
try {
const searchTerm = mpSearch.trim();
// Generate multiple search variations for case-insensitive matching:
// 1. Title case: "adam van" -> "Adam Van"
const titleCase = searchTerm
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
// 2. First word capitalized only: "adam van" -> "Adam van" (for names like "Adam van Koeverden")
const firstWordCapitalized = searchTerm
.split(' ')
.map((word, i) => i === 0 ? word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() : word.toLowerCase())
.join(' ');
// 3. Lowercase: "Adam Van" -> "adam van"
const lowerCase = searchTerm.toLowerCase();
// Query Neo4j GraphQL for MPs with multiple search variations
const graphqlEndpoint = process.env.NEXT_PUBLIC_GRAPHQL_URL || 'http://localhost:4000/graphql';
const apiKey = process.env.NEXT_PUBLIC_GRAPHQL_API_KEY;
const response = await fetch(graphqlEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(apiKey ? { 'X-API-Key': apiKey } : {}),
},
body: JSON.stringify({
query: `
query SearchMPs($s1: String!, $s2: String!, $s3: String!, $s4: String!) {
mps(where: { current: true, OR: [
{ name_CONTAINS: $s1 },
{ name_CONTAINS: $s2 },
{ name_CONTAINS: $s3 },
{ name_CONTAINS: $s4 }
] }, options: { limit: 10 }) {
parl_mp_id
name
party
riding
photo_url
}
}
`,
variables: {
s1: searchTerm, // Original input
s2: titleCase, // Title Case
s3: firstWordCapitalized, // First word capitalized
s4: lowerCase // all lowercase
},
}),
});
const json = await response.json();
if (json.data?.mps) {
setSearchResults(json.data.mps);
}
} catch (err) {
console.error('Error searching MPs:', err);
alert('Failed to search MPs. Please try again.');
} finally {
setIsSearching(false);
}
};
// Submit verification request
const handleSubmit = async () => {
if (!selectedMP) {
alert('Please select an MP from the search results');
return;
}
if (!user?.email) {
alert('No email associated with your account');
return;
}
setIsSubmitting(true);
try {
const result = await requestMPVerification({
mpId: selectedMP.parl_mp_id,
mpName: selectedMP.name,
mpRiding: selectedMP.riding,
mpParty: selectedMP.party,
email: user.email,
proofType: 'parliament_email',
proofNotes: undefined,
});
if (result.success) {
if (result.data?.type === 'email') {
alert(
`Verification email sent to ${user.email}. Please check your inbox and click the verification link.`
);
} else {
alert(
'Verification request submitted. Our team will review your request and contact you within 48 hours.'
);
}
// Refresh status
await fetchVerificationStatus();
// Reset form
setSelectedMP(null);
} else {
alert(result.error || 'Failed to submit verification request');
}
} catch (err) {
console.error('Error submitting verification:', err);
alert('Failed to submit verification request. Please try again.');
} finally {
setIsSubmitting(false);
}
};
// Cancel pending request
const handleCancelRequest = async () => {
if (!confirm('Are you sure you want to cancel your verification request?')) {
return;
}
const result = await cancelMPVerificationRequest();
if (result.success) {
alert('Verification request cancelled');
await fetchVerificationStatus();
} else {
alert(result.error || 'Failed to cancel request');
}
};
if (!user) {
return (
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="bg-background-secondary border-2 border-border-primary rounded-lg p-8 text-center">
<AlertCircle className="mx-auto mb-4 text-accent-red" size={48} />
<h2 className="text-2xl font-bold mb-4">Sign In Required</h2>
<p className="text-text-secondary mb-6">
You must be signed in to request MP verification.
</p>
<button
onClick={() => router.push('/auth/login')}
className="px-6 py-3 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all"
>
Sign In
</button>
</div>
</div>
);
}
// Success message (email verified)
if (success === 'true') {
return (
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="bg-green-50 border-2 border-green-500 rounded-lg p-8 text-center">
<CheckCircle className="mx-auto mb-4 text-green-500" size={48} />
<h2 className="text-2xl font-bold mb-4 text-green-900">Verification Successful!</h2>
<p className="text-green-800 mb-6">
Congratulations, <strong>{mpName}</strong>! Your MP status has been verified.
</p>
<div className="flex items-center justify-center gap-2 mb-6">
<span className="text-lg text-gray-700">You now have a verified badge:</span>
<MPVerifiedBadge size={24} />
</div>
<button
onClick={() => router.push('/forum')}
className="px-6 py-3 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all"
>
Go to Forum
</button>
</div>
</div>
);
}
// Error message
if (error) {
return (
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="bg-red-50 border-2 border-red-500 rounded-lg p-8 text-center">
<AlertCircle className="mx-auto mb-4 text-red-500" size={48} />
<h2 className="text-2xl font-bold mb-4 text-red-900">Verification Failed</h2>
<p className="text-red-800 mb-6">
{error === 'missing-token'
? 'Invalid verification link. Please request a new verification email.'
: error === 'expired'
? 'Verification link has expired. Please request a new verification email.'
: 'Verification failed. Please try again or contact support.'}
</p>
<button
onClick={() => router.push('/verify-mp')}
className="px-6 py-3 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all"
>
Try Again
</button>
</div>
</div>
);
}
// Already verified
if (verificationStatus?.is_verified && !statusLoading) {
return (
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="bg-background-secondary border-2 border-green-500 rounded-lg p-8">
<div className="flex items-center gap-3 mb-4">
<CheckCircle className="text-green-500" size={32} />
<h2 className="text-2xl font-bold">Already Verified</h2>
</div>
<p className="text-text-secondary mb-4">
You are already verified as <strong>{verificationStatus.mp_name}</strong> (
{verificationStatus.mp_riding}).
</p>
<div className="flex items-center gap-2 mb-6">
<span>Your verified badge:</span>
<MPVerifiedBadge size={20} />
</div>
<button
onClick={() => router.push('/forum')}
className="px-6 py-3 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all"
>
Go to Forum
</button>
</div>
</div>
);
}
// Pending request
if (verificationStatus?.has_pending_request && !statusLoading) {
return (
<div className="container mx-auto px-4 py-8 max-w-2xl">
<div className="bg-background-secondary border-2 border-yellow-500 rounded-lg p-8">
<div className="flex items-center gap-3 mb-4">
<Loader2 className="text-yellow-500 animate-spin" size={32} />
<h2 className="text-2xl font-bold">Verification Pending</h2>
</div>
<p className="text-text-secondary mb-4">
Your verification request is being reviewed by our team. You will be notified once the
review is complete.
</p>
<p className="text-sm text-text-tertiary mb-6">
This usually takes 24-48 hours during business days.
</p>
<button
onClick={handleCancelRequest}
className="px-6 py-3 border-2 border-border-primary text-text-secondary rounded-lg hover:bg-background-primary transition-all"
>
Cancel Request
</button>
</div>
</div>
);
}
// Main verification request form
return (
<div className="container mx-auto px-4 py-8 max-w-3xl">
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">MP Verification</h1>
<p className="text-text-secondary">
Verify your identity as a Member of Parliament to receive a verified badge on your
profile.
</p>
</div>
{/* Benefits Section */}
<div className="bg-blue-50 border-2 border-blue-200 rounded-lg p-6 mb-8">
<h3 className="font-bold mb-3 text-blue-900">Benefits of Verification</h3>
<ul className="space-y-2 text-blue-800 text-sm">
<li className="flex items-start gap-2">
<CheckCircle size={16} className="mt-0.5 flex-shrink-0" />
<span>Receive a blue verified badge next to your name</span>
</li>
<li className="flex items-start gap-2">
<CheckCircle size={16} className="mt-0.5 flex-shrink-0" />
<span>Build trust with constituents and citizens</span>
</li>
<li className="flex items-start gap-2">
<CheckCircle size={16} className="mt-0.5 flex-shrink-0" />
<span>Engage directly in discussions about bills and debates</span>
</li>
<li className="flex items-start gap-2">
<CheckCircle size={16} className="mt-0.5 flex-shrink-0" />
<span>Distinguish your official voice from impersonators</span>
</li>
</ul>
</div>
{/* Step 1: Search for MP */}
<div className="bg-background-secondary border-2 border-border-primary rounded-lg p-6 mb-6">
<h3 className="font-bold mb-4 flex items-center gap-2">
<span className="bg-accent-red text-white rounded-full w-6 h-6 flex items-center justify-center text-sm">
1
</span>
Find Your MP Profile
</h3>
<div className="flex gap-2 mb-4">
<input
type="text"
value={mpSearch}
onChange={(e) => setMPSearch(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
placeholder="Enter your name (e.g., Justin Trudeau)"
className="flex-1 px-4 py-2 border-2 border-border-primary rounded-lg bg-background-primary text-gray-900 placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-accent-red"
disabled={isSearching}
/>
<button
onClick={handleSearch}
disabled={isSearching || !mpSearch.trim()}
className="px-6 py-2 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{isSearching ? <Loader2 className="animate-spin" size={20} /> : <Search size={20} />}
Search
</button>
</div>
{/* Search Results */}
{searchResults.length > 0 && (
<div className="space-y-2">
{searchResults.map((mp) => (
<div
key={mp.parl_mp_id}
onClick={() => setSelectedMP(mp)}
className={`
p-4 border-2 rounded-lg cursor-pointer transition-all
${
selectedMP?.parl_mp_id === mp.parl_mp_id
? 'border-accent-red bg-red-50'
: 'border-border-primary hover:border-accent-red hover:bg-background-primary'
}
`}
>
<div className="flex items-center gap-3">
{mp.photo_url && (
<img
src={mp.photo_url}
alt={mp.name}
className="w-12 h-12 rounded-full object-cover"
/>
)}
<div>
<div className={`font-bold ${selectedMP?.parl_mp_id === mp.parl_mp_id ? 'text-gray-900' : 'text-white'}`}>{mp.name}</div>
<div className={`text-sm ${selectedMP?.parl_mp_id === mp.parl_mp_id ? 'text-gray-600' : 'text-gray-400'}`}>
{mp.party} • {mp.riding}
</div>
</div>
{selectedMP?.parl_mp_id === mp.parl_mp_id && (
<CheckCircle className="ml-auto text-accent-red" size={24} />
)}
</div>
</div>
))}
</div>
)}
</div>
{/* Step 2: Verification Method */}
{selectedMP && (
<div className="bg-background-secondary border-2 border-border-primary rounded-lg p-6 mb-6">
<h3 className="font-bold mb-4 flex items-center gap-2">
<span className="bg-accent-red text-white rounded-full w-6 h-6 flex items-center justify-center text-sm">
2
</span>
Choose Verification Method
</h3>
{/* Email Verification */}
<div className="p-4 border-2 rounded-lg border-accent-red bg-red-50">
<div className="flex items-start gap-3">
<Mail className="mt-1 flex-shrink-0 text-gray-700" size={24} />
<div className="flex-1">
<div className="font-bold mb-1 text-gray-900">Email Verification</div>
<p className="text-sm mb-3 text-gray-600">
A verification email will be sent to your account email.
</p>
<div className="px-4 py-2 border-2 border-gray-300 rounded-lg bg-gray-100 text-gray-900">
{user?.email || 'No email on account'}
</div>
</div>
<CheckCircle className="text-accent-red flex-shrink-0" size={24} />
</div>
</div>
{/* Manual Verification - Commented out for now
<div
onClick={() => setProofType('manual')}
className={`
p-4 border-2 rounded-lg cursor-pointer transition-all
${
proofType === 'manual'
? 'border-accent-red bg-red-50'
: 'border-border-primary hover:border-accent-red'
}
`}
>
<div className="flex items-start gap-3">
<FileText className="mt-1 flex-shrink-0 text-gray-700" size={24} />
<div className="flex-1">
<div className="font-bold mb-1 text-gray-900">Manual Verification</div>
<p className="text-sm text-gray-600 mb-3">
Submit alternative proof for admin review (24-48 hours).
</p>
{proofType === 'manual' && (
<div className="space-y-2">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Your contact email"
className="w-full px-4 py-2 border-2 border-gray-300 rounded-lg bg-white text-gray-900 placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-accent-red"
/>
<textarea
value={proofNotes}
onChange={(e) => setProofNotes(e.target.value)}
placeholder="Describe your proof of identity (e.g., office phone number, social media verification, etc.)"
rows={4}
className="w-full px-4 py-2 border-2 border-gray-300 rounded-lg bg-white text-gray-900 placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-accent-red resize-none"
/>
</div>
)}
</div>
{proofType === 'manual' && (
<CheckCircle className="text-accent-red flex-shrink-0" size={24} />
)}
</div>
</div>
*/}
</div>
)}
{/* Submit Button */}
{selectedMP && (
<div className="flex justify-end">
<button
onClick={handleSubmit}
disabled={isSubmitting || !user?.email}
className="px-8 py-3 bg-accent-red text-white rounded-lg hover:bg-red-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{isSubmitting && <Loader2 className="animate-spin" size={20} />}
{isSubmitting ? 'Submitting...' : 'Send Verification Email'}
</button>
</div>
)}
</div>
);
}